package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.tianji.learning.constants.RedisConstants.POINTS_BOARD_KEY_PREFIX;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author xioayang
 * @since 2024-06-30
 */
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {
        private final StringRedisTemplate redisTemplate;
    /**
     * 1、添加积分记录（Day7 - 课堂）
     *
     * @param userId
     * @param points
     * @param type
     */
    @Override
    public void addPointsRecord(Long userId, int points, PointsRecordType type) {
        // 1、判断当前的积分方式是否有积分上限
        int maxPoints = type.getMaxPoints();  // 50
        LocalDateTime now = LocalDateTime.now();
        if (maxPoints > 0) {
            // 2、如果有，则判断当天该用户已获取积分总额是否超过每日上限
            // 2.1、查询【当前用户】【今日】在该【积分类型】下已得积分【总数】
            // select sum(points) as points from points_record where user_id = ? and type = ? and create_time between ? and ?
            LocalDateTime begin = DateUtils.getDayStartTime(now);
            LocalDateTime end = DateUtils.getDayEndTime(now);
            int currentDayPoints = queryUserPointsByTypeAndDate(userId, type, begin, end);

            // 2.2、判断是否超过每日上限
            if(currentDayPoints >= maxPoints){
                // 2.3、超过，直接结束
                return;
            }

            // 2.3、处理已有积分 + 本次待加积分 > 积分上限的情况，不能直接加了，而是要加上  上限 - 已有积分的差值部分
            if(currentDayPoints + points > maxPoints){
                points = maxPoints - currentDayPoints;
            }
        }

        // 3、如果没有，则直接保存积分记录
        PointsRecord pointsRecord = new PointsRecord();
        pointsRecord.setPoints(points);
        pointsRecord.setUserId(userId);
        pointsRecord.setType(type);
        save(pointsRecord);
        // 4.更新当前用户的总积分值到redis的zset结构中
        //4.1组装zset排行榜key（boards:202407
        String key =POINTS_BOARD_KEY_PREFIX+now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        //4.2累加积分
        redisTemplate.opsForZSet().incrementScore(key,userId.toString(),points);

    }

    /**
     * 2、查询积分记录
     *
     * @return
     */
    @Override
    public List<PointsStatisticsVO> querMyPointsToday() {
        // 1、查询统计当前用户当天积分获取情况（分组统计）
        //SELECT `type`,SUM(points) FROM points_record
        // WHERE user_id = ? AND create_time BETWEEN ? AND ? group by `type`
        LocalDateTime now = LocalDateTime.now();
        List<PointsRecord> list = this.query()
                .select("`type`,SUM(points) as points")
                .eq("user_id", UserContext.getUser())
                .between("create_time", DateUtils.getDayStartTime(now), DateUtils.getDayEndTime(now))
                .groupBy("`type`")
                .list();

        // 2、封装成VO返回
        List<PointsStatisticsVO> voList = new ArrayList<>(list.size());
        for (PointsRecord pointsRecord : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(pointsRecord.getType().getDesc());
            vo.setMaxPoints(pointsRecord.getType().getMaxPoints());
            vo.setPoints(pointsRecord.getPoints());
            voList.add(vo);
        }

        // 3、处理没有查询结果的类型
        Set<String> descSet = voList.stream().map(vo -> vo.getType()).collect(Collectors.toSet());
        List<PointsRecordType> types = Arrays.stream(PointsRecordType.values()).filter(p -> !descSet.contains(p.getDesc())).collect(Collectors.toList());
        for (PointsRecordType type : types) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(type.getDesc());
            vo.setMaxPoints(type.getMaxPoints());
            vo.setPoints(0);
            voList.add(vo);
        }
        return voList;
    }

    private int queryUserPointsByTypeAndDate(Long userId, PointsRecordType type, LocalDateTime begin, LocalDateTime end) {
        LambdaQueryWrapper<PointsRecord> queryWrapper = new LambdaQueryWrapper();
        queryWrapper.eq(PointsRecord::getUserId, userId)
                .eq(PointsRecord::getType, type)
                .between(PointsRecord::getCreateTime, begin, end);
        Integer points = this.getBaseMapper().queryUserPointsByTypeAndDate(queryWrapper);
        return points == null? 0: points;    }
}
